#include <dspSimulator/dspSimulator/dspSimulator.h>
#include <dspSimulator/targetMachine/dspRegisterInfo.h>
#include <dspSimulator/instruction/add.h>
#include <dspSimulator/instruction/addad.h>
#include <dspSimulator/instruction/addaw.h>
#include <dspSimulator/instruction/addk.h>
#include <dspSimulator/instruction/addkpc.h>
#include <dspSimulator/instruction/addu.h>
#include <dspSimulator/instruction/and.h>
#include <dspSimulator/instruction/andn.h>
#include <dspSimulator/instruction/b.h>
#include <dspSimulator/instruction/bnop.h>
#include <dspSimulator/instruction/callp.h>
#include <dspSimulator/instruction/clr.h>
#include <dspSimulator/instruction/cmpeq.h>
#include <dspSimulator/instruction/cmpgt.h>
#include <dspSimulator/instruction/cmpgtu.h>
#include <dspSimulator/instruction/cmplt.h>
#include <dspSimulator/instruction/cmpltu.h>
#include <dspSimulator/instruction/dadd.h>
#include <dspSimulator/instruction/dint.h>
#include <dspSimulator/instruction/ext.h>
#include <dspSimulator/instruction/extu.h>
#include <dspSimulator/instruction/mv.h>
#include <dspSimulator/instruction/mvc.h>
#include <dspSimulator/instruction/mvk.h>
#include <dspSimulator/instruction/mvkh.h>
#include <dspSimulator/instruction/nop.h>
#include <dspSimulator/instruction/or.h>
#include <dspSimulator/instruction/pack2.h>
#include <dspSimulator/instruction/packl4.h>
#include <dspSimulator/instruction/rint.h>
#include <dspSimulator/instruction/set.h>
#include <dspSimulator/instruction/shl.h>
#include <dspSimulator/instruction/shr.h>
#include <dspSimulator/instruction/shru.h>
#include <dspSimulator/instruction/spkernel.h>
#include <dspSimulator/instruction/sploop.h>
#include <dspSimulator/instruction/sploopd.h>
#include <dspSimulator/instruction/sploopw.h>
#include <dspSimulator/instruction/spmask.h>
#include <dspSimulator/instruction/stb.h>
#include <dspSimulator/instruction/sth.h>
#include <dspSimulator/instruction/stw.h>
#include <dspSimulator/instruction/stndw.h>
#include <dspSimulator/instruction/stnw.h>
#include <dspSimulator/instruction/stdw.h>
#include <dspSimulator/instruction/sub.h>
#include <dspSimulator/instruction/subu.h>
#include <dspSimulator/instruction/subaw.h>
#include <dspSimulator/instruction/subc.h>
#include <dspSimulator/instruction/ldb.h>
#include <dspSimulator/instruction/ldbu.h>
#include <dspSimulator/instruction/ldh.h>
#include <dspSimulator/instruction/ldw.h>
#include <dspSimulator/instruction/ldnw.h>
#include <dspSimulator/instruction/lddw.h>
#include <dspSimulator/instruction/ldndw.h>
#include <dspSimulator/instruction/lmbd.h>
#include <dspSimulator/instruction/mpyus.h>
#include <dspSimulator/instruction/mpy32u.h>
#include <dspSimulator/instruction/neg.h>
#include <dspSimulator/instruction/xor.h>
#include <simulator/report/report.h>
#include <dspSimulator/dspSimulator/dspInstFetcher.h>
#include <simulator/bitTools/bitTools.h>
#include <iostream>

void DSPSimulator::addAllInstructionProto() {
    m_instProto.push_back(std::make_shared<MVK>());
    m_instProto.push_back(std::make_shared<MVKH>());
    m_instProto.push_back(std::make_shared<AND>());
    m_instProto.push_back(std::make_shared<MVC>());
    m_instProto.push_back(std::make_shared<NOP>());
    m_instProto.push_back(std::make_shared<B>());
    m_instProto.push_back(std::make_shared<ADDKPC>());
    m_instProto.push_back(std::make_shared<STB>());
    m_instProto.push_back(std::make_shared<STH>());
    m_instProto.push_back(std::make_shared<STW>());
    m_instProto.push_back(std::make_shared<STDW>());
    m_instProto.push_back(std::make_shared<STNDW>());
    m_instProto.push_back(std::make_shared<LDB>());
    m_instProto.push_back(std::make_shared<LDH>());
    m_instProto.push_back(std::make_shared<LDW>());
    m_instProto.push_back(std::make_shared<LDNDW>());
    m_instProto.push_back(std::make_shared<LDDW>());
    m_instProto.push_back(std::make_shared<MV>());
    m_instProto.push_back(std::make_shared<CMPEQ>());
    m_instProto.push_back(std::make_shared<CMPGTU>());
    m_instProto.push_back(std::make_shared<BNOP>());
    m_instProto.push_back(std::make_shared<SUB>());
    m_instProto.push_back(std::make_shared<SHR>());
    m_instProto.push_back(std::make_shared<ADD>());
    m_instProto.push_back(std::make_shared<ADDK>());
    m_instProto.push_back(std::make_shared<NEG>());
    m_instProto.push_back(std::make_shared<PACK2>());
    m_instProto.push_back(std::make_shared<PACKL4>());
    m_instProto.push_back(std::make_shared<CMPLTU>());
    m_instProto.push_back(std::make_shared<SHRU>());
    m_instProto.push_back(std::make_shared<MPYUS>());
    m_instProto.push_back(std::make_shared<OR>());
    m_instProto.push_back(std::make_shared<SHL>());
    m_instProto.push_back(std::make_shared<EXTU>());
    m_instProto.push_back(std::make_shared<SPLOOP>());
    m_instProto.push_back(std::make_shared<SPLOOPW>());
    m_instProto.push_back(std::make_shared<SPMASK>());
    m_instProto.push_back(std::make_shared<CALLP>());
    m_instProto.push_back(std::make_shared<LDBU>());
    m_instProto.push_back(std::make_shared<SPKERNEL>());
    m_instProto.push_back(std::make_shared<STNW>());
    m_instProto.push_back(std::make_shared<XOR>());
    m_instProto.push_back(std::make_shared<SET>());
    m_instProto.push_back(std::make_shared<ANDN>());
    m_instProto.push_back(std::make_shared<CMPGT>());
    m_instProto.push_back(std::make_shared<SUBAW>());
    m_instProto.push_back(std::make_shared<DADD>());
    m_instProto.push_back(std::make_shared<CMPLT>());
    m_instProto.push_back(std::make_shared<DINT>());
    m_instProto.push_back(std::make_shared<RINT>());
    m_instProto.push_back(std::make_shared<ADDAW>());
    m_instProto.push_back(std::make_shared<CLR>());
    m_instProto.push_back(std::make_shared<EXT>());
    m_instProto.push_back(std::make_shared<LMBD>());
    m_instProto.push_back(std::make_shared<SUBC>());
    m_instProto.push_back(std::make_shared<MPY32U>());
    m_instProto.push_back(std::make_shared<SUBU>());
    m_instProto.push_back(std::make_shared<ADDU>());
    m_instProto.push_back(std::make_shared<LDNW>());
    m_instProto.push_back(std::make_shared<ADDAD>());
    m_instProto.push_back(std::make_shared<SPLOOPD>());
}

void DSPSimulator::run() {
    // 用户的命令输入
    std::string command;
    // 指令数据信息
    std::shared_ptr<InstInfo> instInfo;

    while (true) {
        // 取出当前指令指针寄存器指向的指令执行包
        auto instInfos = fetch();
        auto executePacket = decode(instInfos);
        m_numOfInstruction += instInfos.size();

        // 打印指令执行包信息
        bool isFirstInstructon = true;
        u64 offset = executePacket.size();
        for (auto &inst: executePacket) {
            if (isFirstInstructon) {
                fprintf(stdout, ">> %08lX : %s\t t=%lu\n", inst->getInstructionAddress(), inst->toString().c_str(),
                        m_numOfInstruction - offset + 1);
                isFirstInstructon = false;
            } else {
                fprintf(stdout, ">> %08lX : || %s\t t=%lu\n", inst->getInstructionAddress(), inst->toString().c_str(),
                        m_numOfInstruction - offset + 1);
            }
            --offset;
        }

        // 用户的输入命令处理
        while (true) {
            fprintf(stdout, ">> ");
            // 获取用户命令
            getline(std::cin, command);
            // 如果输入n，执行当前指令，并解码下一条指令
            if (command == "n") {
                NextCommand::get(this, executePacket)->execute(command);
                break;
            } else if (command == "A") {
                PrintARegsCommand::get(this)->execute(command);
            } else if (command == "B") {
                PrintBRegsCommand::get(this)->execute(command);
            } else if (command == "quit" || command == "exit" || command == "q") {
                ExitCommand::get(this)->execute(command);
            } else if (command.find("regA") == 0) {
                PrintARegCommand::get(this)->execute(command);
            } else if (command.find("regB") == 0) {
                PrintBRegCommand::get(this)->execute(command);
            } else if (command.find("cReg") == 0) {
                PrintCRegCommand::get(this)->execute(command);
            } else if (command.find('b') == 0) {
                BreakPointCommand::get(this)->execute(command);
            } else if (command.find('m') == 0) {
                WatchMemoryCommand::get(this)->execute(command);
            } else if (command == "c" || command == "continue") {
                ContinueCommand::get(this, executePacket)->execute(command);
            } else if (command == "save" || command == "restore") {
                // 恢复pc寄存器数据
                auto pcReg = m_targetMachine->getRegisters()->getReg32ByName("pc");
                for (auto &instruction: executePacket) {
                    pcReg->updateData(*pcReg->getDataPtr() - (instruction->isDelayInstruction() ? 2 : 4));
                }
                m_numOfInstruction -= executePacket.size();
                SaveAndRestoreCommand::get(this)->execute(command);
                break;
            } else {
                fprintf(stdout, "%s\n", "未识别的命令，请重新输入!");
            }
        }
    }
}

void DSPSimulator::executeInsertNOP(u32 nopNum) {
    if (nopNum == 0)
        return;

    for (u32 i = 0; i < nopNum; i++) {
        auto nop = NOP::getSpecialNOP(1);
        std::vector<std::shared_ptr<Instruction>> executePacket;
        executePacket.push_back(nop);
        execute(executePacket);
    }
}

void DSPSimulator::beforeInstructionExecute(std::vector<std::shared_ptr<Instruction>> &executePacket) {
    // 如果当前阶段为Drain阶段，将buffer中接下来一个阶段的指令标记为被屏蔽的
    if (m_sploopBuffer->isDrain()) {
        m_sploopBuffer->drainOneStage();
    }

    std::vector<std::shared_ptr<DspInstruction>> instructions;
    // 如果当前阶段为fetch阶段
    if (m_sploopBuffer->isFetch()) {
        // 从sploop buffer中取出当前周期需要执行的额外的指令，并加载到当前周期的执行包中
        instructions = m_sploopBuffer->fetchInstructions();
    }

    // 如果当前周期为load阶段，即Prolog阶段和第一个Kernel阶段K0, 加载指令到loop buffer中
    if (m_sploopBuffer->isLoad()) {
        m_sploopBuffer->loadExecutePacketIntoBuffer(executePacket);
    }

    for (auto &instruction: instructions) {
        // 将指令的执行周期设置为当前周期
        instruction->updateBaseCycle(m_timers[0]->getCycle());
        executePacket.push_back(instruction);
    }
}

void DSPSimulator::afterInstructionExecute(std::vector<std::shared_ptr<Instruction>> &executePacket) {
    // 在每个周期的最后，如果启动了sploop，lbc +1
    if (m_sploopBuffer->isActive()) {
        m_sploopBuffer->incrementLBC();
    }
}

void DSPSimulator::beforeNextExecutePacketExecute(std::vector<std::shared_ptr<Instruction>> &executePacket) {
    /// 程序执行到此行代码时，意味着前一个周期的所有事已经完成
    /// 设置当前周期的各种状态信息

    // 如果前一个周期的第一条指令为sploop系列指令，当前周期应为load阶段
    if (!executePacket.empty() &&
        (executePacket[0]->getName() == "SPLOOP" ||
         executePacket[0]->getName() == "SPLOOPD" ||
         executePacket[0]->getName() == "SPLOOPW")) {
        auto sploopInstruction = std::dynamic_pointer_cast<DspInstruction>(executePacket[0]);
        m_sploopBuffer->initial(sploopInstruction);
        m_sploopBuffer->setLoad(true);
    }

    // 如果已经经历过一个load阶段，当前阶段为fetch阶段
    if (m_sploopBuffer->isActive() && m_sploopBuffer->getNumOfLoad() == 1) {
        m_sploopBuffer->setFetch(true);
    }
    // 如果上一个周期的最后一个执行包的首指令为spkernel指令，当前周期应关闭load阶段
    if (m_sploopBuffer->isActive() && !executePacket.empty() && executePacket[0]->getName() == "SPKERNEL") {
        m_sploopBuffer->setLoad(false);
    }

    /**
     * 如果SPLoop buffer处于激活状态，且spLoop已完成（ILC ==0 || 条件为true)
     */
    if (m_sploopBuffer->isTerminate()) {
        auto dspInstFetcher = std::dynamic_pointer_cast<DspInstFetcher>(m_instFetcher);
        switch (m_sploopBuffer->getSPLoopKind()) {
            case SPLoopKind::SPLoop:
            case SPLoopKind::SPLoopD:
                // 启动Drain阶段
                m_sploopBuffer->setDrain(true);
                // 关闭锁存
//                dspInstFetcher->setFetchState(true);
                // 设置epilog阶段的延时，用nop指令实现
//                numOfInsertNOP = m_sploopBuffer->getDelay();
                break;
            case SPLoopKind::SPLoopW:
                // 如果是sploopw启动的buffer，当条件满足时，三个周期后才关闭sploop buffer
                static u32 delay = 2;
                if (--delay == 0) {
                    m_sploopBuffer->disableSPLoopBuffer();
                    dspInstFetcher->setFetchState(true);
                    delay = 2;
                }
            default:
                break;
        }
    }
    // sploop and sploopd,如果不为load阶段，且指令已drain完毕，关闭sploopbuffer
    if (m_sploopBuffer->isActive() && !m_sploopBuffer->isLoad() &&
        m_sploopBuffer->haveFinishDrain()) {
        // 关闭sploop buffer
        m_sploopBuffer->disableSPLoopBuffer();
        auto dspInstFetcher = std::dynamic_pointer_cast<DspInstFetcher>(m_instFetcher);
        // 关闭锁存
        dspInstFetcher->setFetchState(true);
    }
}

std::vector<std::shared_ptr<InstInfo>> DSPSimulator::fetch() {
    std::vector<std::shared_ptr<InstInfo>> executePacket;
    while (true) {
        // 取出pc指向的指令数据
        auto instInfo = m_instFetcher->fetchOneInstruction();
        // 如果未能从取出指令，返回一个空的执行包
        if (instInfo == nullptr) {
            return {};
        }
        std::shared_ptr<Instruction> instruction;
        // 解码code，更新m_inst对象
        for (auto &instProto: m_instProto) {
            instruction = instProto->decode(instInfo, m_timers[0]->getCycle());
            // 如果解码成功，退出循环
            if (instruction)
                break;
        }

        // 如果instruction为空，表示解码失败，打印错误信息
        if (!instruction) {
            fprintf(stdout, "指令解码失败，未知类型的指令地址为: %08lX\n", instInfo->getAddress());
            exit(0);
        }
        // 向执行包中添加当前的指令数据
        executePacket.push_back(instInfo);
        // 如果当前指令的p位为0，继续取出下一条指令数据，否则取指完毕
        auto dspInstruction = std::dynamic_pointer_cast<DspInstruction>(instruction);
        if (!dspInstruction->p)
            break;
    }
    return executePacket;
}

std::vector<std::shared_ptr<Instruction>> DSPSimulator::decode(std::vector<std::shared_ptr<InstInfo>> instInfos) {
    std::vector<std::shared_ptr<Instruction>> executePacket;
    for (auto &instInfo: instInfos) {
        std::shared_ptr<Instruction> instruction;
        for (auto &instProto: m_instProto) {
            instruction = instProto->decode(instInfo, m_timers[0]->getCycle());
            // 如果解码成功，退出循环
            if (instruction)
                break;
        }
        executePacket.push_back(instruction);
    }
    return executePacket;
}

void DSPSimulator::execute(std::vector<std::shared_ptr<Instruction>> instructions) {
    // 延时指令的周期延时
    std::list<u64> allCycle;
    // 需要插入到当前执行包后的nop指令数
    m_numOfInsertNOP = 0;
    std::shared_ptr<DspMachine> dspMachine = std::dynamic_pointer_cast<DspMachine>(m_targetMachine);
    // 用于保存执行包执行前的寄存器状态
    dspMachine->updateSaveRegs();

//    // 如果当前周期未能从内存中读取指令，结束程序
//    if (instructions.empty()) {
//        fprintf(stdout, "程序运行结束");
//        exit(0);
//    }

    /// 进入当前周期执行

    // 当前执行包执行前的插桩函数
    beforeInstructionExecute(instructions);

    for (auto &instruction: instructions) {
        auto dspInstruction = std::dynamic_pointer_cast<DspInstruction>(instruction);
        if (dspInstruction->getName() == "SPLOOP"
            || dspInstruction->getName() == "SPLOOPW"
            || dspInstruction->getName() == "SPLOOPD") {
            continue;
        } else if (dspInstruction->getName() == "SPKERNEL") {
            // 设置fcyc与fstg
            m_sploopBuffer->setFstgAndFcyc(dspInstruction->fstg, dspInstruction->fcyc);
//            numOfInsertNOP = m_sploopBuffer->getDelay();
//            // 如果sploop buffer处于激活状态，锁存，下一个取指令不从内存中取出
            if (m_sploopBuffer->isActive()) {
                auto dspFetcher = std::dynamic_pointer_cast<DspInstFetcher>(m_instFetcher);
                dspFetcher->setFetchState(false);
            }
        } else {
            if (dspInstruction->checkCondition(dspMachine)) {
                if (dspInstruction->getName() == "B"
                    || dspInstruction->getName() == "BNOP"
                    || dspInstruction->getName() == "CallP") {
                    m_sploopBuffer->disableSPLoopBuffer();
                }

                dspInstruction->execute(dspMachine, m_llvmJit,
                                        static_cast<ExecutePhase>(m_timers[0]->getCycle() -
                                                                  dspInstruction->getBaseCycle()));
                if (dspInstruction->isDelayInstruction()) {
                    addInstIntoDelayQueue(dspInstruction);
                }
            }
        }
        // 更新当前执行包需要插入的nop指令数
        u32 curNopNum = dspInstruction->numOfInsertNOP;
        m_numOfInsertNOP = curNopNum == 0 ? m_numOfInsertNOP : curNopNum;
    }

// 如果当前周期有延时指令，执行延时指令
    executeDelayInst();

// 当前周期的指令执行完毕，接下来完成指令执行完之后的任务
    afterInstructionExecute(instructions);


// 进入下一个周期
    nextCycle();


    // 当前周期的执行包执行之前，使用上一个执行周期的状态信息，更新当前周期的状态所需要的插桩函数
    beforeNextExecutePacketExecute(instructions);

    // 执行插入在当前执行包之后的nop指令
    executeInsertNOP(m_numOfInsertNOP);

    // 处理中断，如果中断存在，终止当前程序执行
    m_interruptHandle->handleInterrupt();
}

void DSPSimulator::addGlobalVariable(llvm::LLVMContext *llvmContext, llvm::Module *llvmModule) {
    // 采用外部链接声明指令数据变量
    llvmModule->getOrInsertGlobal("src1LoRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("src1HiRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("src2LoRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("src2HiRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("dstLoRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("dstHiRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("cRegPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("cstPtr", llvm::Type::getInt32PtrTy(*llvmContext));
    llvmModule->getOrInsertGlobal("auxCstPtr", llvm::Type::getInt32PtrTy(*llvmContext));

//    llvmModule->print(llvm::errs(), nullptr);
}

void DSPSimulator::addGlobalMapping(std::shared_ptr<llvm::ExecutionEngine> executionEngine) {
    // 将包含指令对应的函数的llvm模块添加到jit引擎中
    executionEngine->addModule(m_llvmJit->extraCurModule());
    executionEngine->addGlobalMapping("src1LoRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::src1LoRegPtr));
    executionEngine->addGlobalMapping("src1HiRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::src1HiRegPtr));
    executionEngine->addGlobalMapping("src2LoRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::src2LoRegPtr));
    executionEngine->addGlobalMapping("src2HiRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::src2HiRegPtr));
    executionEngine->addGlobalMapping("dstLoRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::dstLoRegPtr));
    executionEngine->addGlobalMapping("dstHiRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::dstHiRegPtr));
    executionEngine->addGlobalMapping("cRegPtr", reinterpret_cast<uint64_t>(&DspInstruction::cRegPtr));
    executionEngine->addGlobalMapping("cstPtr", reinterpret_cast<uint64_t>(&DspInstruction::cstPtr));
    executionEngine->addGlobalMapping("auxCstPtr", reinterpret_cast<uint64_t>(&DspInstruction::auxCstPtr));
}


void DSPSimulator::restore(std::shared_ptr<Snapshot> snapshot) {
    m_targetMachine->updateMMU(snapshot->getMMU());
    m_targetMachine->updateRegisterMap(snapshot->getRegisterMap());
    m_numOfInstruction = snapshot->getNumOfInstruction();
}

std::shared_ptr<Snapshot> DSPSimulator::createSnapshot() {
    return std::make_shared<Snapshot>(m_targetMachine.get(), m_numOfInstruction);
}
